package org.xxpay.pay.ctrl.payment;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.xxpay.core.common.constant.MchConstant;
import org.xxpay.core.common.constant.PayConstant;
import org.xxpay.core.common.constant.RetEnum;
import org.xxpay.core.common.domain.api.AbstractRes;
import org.xxpay.core.common.util.*;
import org.xxpay.core.entity.*;
import org.xxpay.pay.channel.alipay.AlipayConfig;
import org.xxpay.pay.ctrl.common.BaseController;
import org.xxpay.pay.service.LocalDynamicChannelService;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.Date;
import java.util.List;

/**
 * 聚合码支付 ctrl
 */
@Controller
@RequestMapping("/api/qrcode")
public class QRcodePayController extends BaseController {

    private final MyLog _log = MyLog.getLog(QRcodePayController.class);

    @Autowired
    private LocalDynamicChannelService localDynamicChannelService;


    /**
     * 用户扫码跳转到输入金额页面
     * 二维码中的参数： 1.mchId  2.qrcodeId
     */
    @RequestMapping("/toQRCodePage")
    public String toQRCodePage(){

        //1. 判断是否来自微信/支付宝的内嵌浏览器
        String ua = request.getHeader("User-Agent");
        if (StringUtils.isBlank(ua)) {  // 无法识别扫码客户端
            return returnErrorPage(RetEnum.RET_MCH_UA_NOT_EXIST);
        }
        //2. 判断必传参数
        JSONObject requestParam = getJsonParam(request); //通过request获取参数，并转换为json格式
        long mchId = getLongRequired(requestParam, "mchId");
        Long qrcodeId = getLongRequired(requestParam, "qrcodeId");
        String appId = getStringRequired(requestParam, "appId");

        //3. 验证数据合法性 (mchId, mchAppId 仅作为二维码校验使用，防止篡改)
        MchQrCode mchQrCode = commonService.mchQrCodeService.findById(qrcodeId);

        if(mchQrCode == null) return returnErrorPage(RetEnum.RET_MCH_CONFIG_NOT_EXIST);
        if(mchId != mchQrCode.getMchId() || !appId.equals(mchQrCode.getAppId())){
            return returnErrorPage(RetEnum.RET_COMM_PARAM_ERROR);
        }

        request.setAttribute("qrcodeId", qrcodeId);
        request.setAttribute("codeName", mchQrCode.getCodeName());
        return "qrcode/qrcode"; //跳转到输入金额页面
    }

    /**
     * 请求下单，并调起微信 / 支付宝的支付插件
     * 微信：
     *      1. 封装参数跳转到微信oauth接口， 微信通过传入的redirectUrl进行回调，请带上了code参数。
     *      2. redirectUrl 监听微信请求，通过code换取openId, 然后进行进行下单操作， 返回支付参数调起微信的 jsapi插件。
     *
     * 支付宝：
     *      1. 直接进行下单操作。
     *
     * @return
     */
    @RequestMapping("/toPay")
    public String toPay(HttpServletRequest request){

        //1. 判断是否来自微信/支付宝的内嵌浏览器
        String ua = request.getHeader("User-Agent");
        if (StringUtils.isBlank(ua)) {  // 无法识别扫码客户端
            return returnErrorPage(RetEnum.RET_MCH_UA_NOT_EXIST);
        }

        //2. 判断必传参数
        JSONObject requestParam = getJsonParam(request); //通过request获取参数，并转换为json格式
        Long qrcodeId = getLongRequired(requestParam, "qrcodeId");
        String amount = getStringRequired(requestParam, "amount");
        Long amountL = Long.parseLong(AmountUtil.convertDollar2Cent(amount));

        String channelId = this.getChannelId(qrcodeId);
        if (channelId == null) { // 无法识别扫码客户端
            return returnErrorPage(RetEnum.RET_MCH_UA_NOT_SUPPORT);
        }

        //3. 验证数据合法性
        if(amountL <= 0 ) return returnErrorPage(RetEnum.RET_COMM_PARAM_ERROR);
        MchQrCode mchQrCode = commonService.mchQrCodeService.findById(qrcodeId);
        if(mchQrCode == null) return returnErrorPage(RetEnum.RET_MCH_CONFIG_NOT_EXIST);
        Long mchId = mchQrCode.getMchId();

        // 判断金额是否符合范围
        if (mchQrCode.getMinAmount().longValue() > amountL.longValue()){
            return returnErrorPage(RetEnum.RET_SERVICE_DRAW_AMOUNT_MIN_LIMIT);
        }

        if (mchQrCode.getMaxAmount().longValue() < amountL.longValue()){
            return returnErrorPage(RetEnum.RET_SERVICE_DRAW_AMOUNT_MAX_LIMIT);
        }

        //根据商户配置， 获取productId
        Integer productId = null;
        JSONArray jsonArray = JSON.parseArray(mchQrCode.getChannels());
        for(Object obj : jsonArray){
            JSONObject jsonObject = (JSONObject)obj;
            if(channelId.equals(jsonObject.getString("channelId"))){
                productId = jsonObject.getInteger("productId");
                break;
            }
        }
        if(productId == null) return returnErrorPage(RetEnum.RET_MCH_CONFIG_NOT_EXIST);

        MchPayPassage mchPayPassage = commonService.mchPayPassageService.findByMchIdAndProductId(mchId, productId);
        if(mchPayPassage == null) {
            // 商户没有配置扫码支付渠道
            return returnErrorPage(RetEnum.RET_MCH_PASSAGE_NOT_EXIST);
        }

        if(mchPayPassage == null) {
            // 商户没有配置扫码支付渠道
            return returnErrorPage(RetEnum.RET_MCH_PASSAGE_NOT_EXIST);
        }
        PayPassageAccount payPassageAccount = null;
        if(mchPayPassage.getIfMode() == 1) {
            // 支付通道ID
            Integer payPassageId = mchPayPassage.getPayPassageId();
            List<PayPassageAccount> payPassageAccountList = commonService.payPassageAccountService.selectAllByPassageId(payPassageId);
            if(CollectionUtils.isEmpty(payPassageAccountList)) {
                return returnErrorPage(RetEnum.RET_MCH_PASSAGE_NOT_EXIST);
            }
            // 需要根据风控规则得到子账户号
            payPassageAccount = payPassageAccountList.get(0);

        }else {
            // 轮询接口

        }

        JSONObject configJSON = JSON.parseObject(payPassageAccount.getParam());
        if(PayConstant.PAY_CHANNEL_WX_JSAPI.equals(channelId)){ //微信
            String wxAppId = configJSON.getString("appId");
            JSONObject returnParam = new JSONObject(); //上送到oauth接口，并原样返回回调地址的参数。
            returnParam.put("qrcodeId", qrcodeId);
            returnParam.put("payAccountId", payPassageAccount.getId());
            returnParam.put("amount", amountL);
            returnParam.put("productId", productId);
            String returnParamStr = MyBase64.encode(returnParam.toJSONString().getBytes()); //base64

            String redirectUrl = String.format("%s/qrcode/wxRedirectUrl/%s", getDBApplicationConfig().getConfig4PayUrl(), returnParamStr);
            String url = String.format(getDBApplicationConfig().getWxOauth2CodeUrl() + "?appid=%s&scope=snsapi_base&state=&redirect_uri=%s",StrUtil.urlEnodeUTF8(wxAppId), StrUtil.urlEnodeUTF8(redirectUrl));
            try {
                response.sendRedirect(url); //请求微信oauth接口
            } catch (IOException e) {
                _log.error("", e);
            }
            return null;

        }else if(PayConstant.PAY_CHANNEL_ALIPAY_WAP.equals(channelId)){ //支付宝 目前支付宝使用的H5方式，无需通过oauth接口获取userID

            JSONObject resultJSONObject = this.createPayOrder(qrcodeId, productId, payPassageAccount.getId(), amountL, null);

            request.setAttribute("state", resultJSONObject.get("state"));
            request.setAttribute("msg", resultJSONObject.get("msg"));

            //将payParams string转换为json格式。
            String payParams = (String)resultJSONObject.get("payParams");
            if(payParams != null){
                JSONObject jsonObject = JSON.parseObject(payParams);
                request.setAttribute("payParams", jsonObject);
            }

            return "qrcode/pay_alipay";

        }else if("alipay_qr_h5".equals(channelId)){
            JSONObject resultJSONObject = this.createPayOrder(qrcodeId, productId, payPassageAccount.getId(), amountL, null);
            request.setAttribute("state", resultJSONObject.get("state"));
            request.setAttribute("msg", resultJSONObject.get("msg"));
            //将payParams string转换为json格式。
            String payParams = (String)resultJSONObject.get("payParams");
            if(payParams != null){
                JSONObject jsonObject = JSON.parseObject(payParams);
                String payOrderId = jsonObject.getString("payOrderId");
                JSONObject obj = new JSONObject();
                obj.put("payOrderId", payOrderId);
                String param = MyBase64.encode(obj.toJSONString().getBytes());
                String toUrl = getDBApplicationConfig().getConfig4PayUrl() + "/alipay/alipay_qr_%s.htm";
                toUrl = String.format(toUrl, param);
                String authUrl = AlipayConfig.authUrl;
                toUrl = authUrl + "?returnUrl=" + toUrl;
                String startAppUrlH5 = "alipays://platformapi/startapp?saId=10000007&clientVersion=3.7.0.0718&qrcode=" + URLEncoder.encode(toUrl);
                request.setAttribute("startAppUrlH5", startAppUrlH5);
            }
            return "qrcode/pay_alipay_qr";
        }
        return null;
    }


    /**
     * 请求微信oauth接口获取用户信息的回调地址（包含code参数）
     * @param returnParamStr 上一步封装的原样参数（base64格式）
     * @return
     * @throws Exception
     */
    @RequestMapping("/wxRedirectUrl/{returnParamStr}")
    public String wxRedirectUrl(HttpServletRequest request, @PathVariable("returnParamStr") String returnParamStr) throws Exception {

        String wxReturnCode = request.getParameter("code"); //微信服务器返回的code, 可以通过code换取accessToken
        JSONObject returnParamObj = JSONObject.parseObject(new String(MyBase64.decode(returnParamStr)));
        Long qrcodeId = returnParamObj.getLong("qrcodeId");
        Integer payAccountId = returnParamObj.getInteger("payAccountId");
        Long amount = returnParamObj.getLong("amount");
        Integer productId = returnParamObj.getInteger("productId");

        // 得到每个渠道配置的微信信息
        WxMpInMemoryConfigStorage wxMpConfigStorage = new WxMpInMemoryConfigStorage();
        PayPassageAccount payPassageAccount = commonService.payPassageAccountService.findById(payAccountId);
        JSONObject configJSON = JSON.parseObject(payPassageAccount.getParam()); //通道子账户的配置信息
        wxMpConfigStorage.setAppId(configJSON.getString("appId"));
        wxMpConfigStorage.setSecret(configJSON.getString("appSecret"));
        WxMpService wxMpService = new WxMpServiceImpl();
        wxMpService.setWxMpConfigStorage(wxMpConfigStorage);
        WxMpOAuth2AccessToken accessToken = wxMpService.oauth2getAccessToken(wxReturnCode);
        String openId = accessToken.getOpenId();

//        // 创建支付订单
        JSONObject resultJSONObject = this.createPayOrder(qrcodeId, productId, payPassageAccount.getId(), amount, openId);
        JSONObject payParams = JsonUtil.getJSONObjectFromJson(resultJSONObject.get("payParams").toString());
        request.setAttribute("state", resultJSONObject.get("state"));
        request.setAttribute("msg", resultJSONObject.get("msg"));
        request.setAttribute("payParams", payParams.getString("payParams"));
        return "qrcode/pay_wx";
    }


    private JSONObject returnFail(JSONObject resultObject, String msg){
        resultObject.put("msg", msg);
        resultObject.put("state", "fail");
        return resultObject;
    }

    /**
     * 创建订单并调用上游下单接口，返回支付所需数据。
     * @param qrcodeId 二维码ID
     * @param productId 支付产品ID
     * @param payAccountId 支付通道账号ID
     * @param amount 订单金额 单位：分
     * @param channelUserId 上游userID, 微信对应的openId
     * @return 支付结果json对象
     */
    private JSONObject createPayOrder(Long qrcodeId, Integer productId, Integer payAccountId, Long amount, String channelUserId) {

        JSONObject resultObject = new JSONObject();

        MchQrCode mchQrCode = commonService.mchQrCodeService.findById(qrcodeId);
        if(mchQrCode == null) return returnFail(resultObject, "二维码信息不存在[qrcodeId="+qrcodeId+"]");
        Long mchId = mchQrCode.getMchId();
        String mchAppId = mchQrCode.getAppId();

        // 验证请求参数有效性（必选项）
        // 查询商户信息
        MchInfo mchInfo = commonService.mchInfoService.findByMchId(mchId);
        if(mchInfo == null) return returnFail(resultObject, "商户不存在[mchId="+mchId+"].");
        if(mchInfo.getStatus() != MchConstant.PUB_YES)  return returnFail(resultObject, "商户状态不可用[mchId="+mchId+"].");

        // 判断请求IP是否允许
        String clintIp = IPUtility.getClientIp(request);
        boolean isAllow = XXPayUtil.ipAllow(clintIp, mchInfo.getPayWhiteIp(), mchInfo.getPayBlackIp());
        if(!isAllow) return returnFail(resultObject, "IP["+clintIp+"]不允许访问");

        // 查询应用信息
        MchApp mchApp = commonService.mchAppService.findByMchIdAndAppId(mchId, mchAppId);
        if(mchApp == null) return returnFail(resultObject, "应用不存在[appId=" + mchAppId + "]");
        if(mchApp.getStatus() != MchConstant.PUB_YES) return returnFail(resultObject, "应用状态不可用[appId=" + mchAppId + "]");

        // 查询商户对应的支付产品
        PayProduct payProduct = commonService.payProductService.findById(productId);
        if(payProduct == null) {
            return returnFail(resultObject, "商户没有该产品的支付通道[productId="+productId+",mchId="+mchId+"]");
        }
        if(payProduct.getStatus() != MchConstant.PUB_YES) {
            return returnFail(resultObject, "商户该产品的支付通道[productId="+productId+",mchId="+mchId+"]已关闭");
        }

        // 支付通道
        PayPassageAccount payPassageAccount = commonService.payPassageAccountService.findById(payAccountId);
        if(payPassageAccount == null || payPassageAccount.getStatus() != MchConstant.PUB_YES) {
            return returnFail(resultObject, "传入子账户不可用[payPassAccountId="+payAccountId+",mchId="+mchId+"]");
        }

        PayPassage payPassage = commonService.payPassageService.findById(payPassageAccount.getPayPassageId());


        Integer passageAccountId = payPassageAccount.getId();
        String channelMchId = payPassageAccount.getPassageMchId();
        String channelType = payPassageAccount.getIfTypeCode();

        String channelId = getChannelId(qrcodeId);

        if(channelType == null || channelId == null) {
            return returnFail(resultObject, "商户没有该产品的支付通道[productId="+productId+",mchId="+mchId+",channelType="+channelType+",channelId="+channelId+"]");
        }

        // 获取商户通道详情
        MchPayPassage mchPayPassage = commonService.mchPayPassageService.findByMchIdAndProductId(mchId, productId);
        // 对应通道子账户
        List<PayPassageAccount> passageAccountList = commonService.payPassageAccountService.selectAllByPassageId(mchPayPassage.getPayPassageId());
        PayPassageAccount passageAccount = null;
        if (passageAccountList.size() > 0) {
            passageAccount = passageAccountList.get(0);
        }
        BigDecimal agentRate = null;
        BigDecimal parentAgentRate = null;
        // 处理二级代理商
        Long agentId = mchInfo.getAgentId();
        Long parentAgentId = mchInfo.getParentAgentId();
        if(agentId != null) {
            AgentPassage agentPassage = commonService.agentPassageService.findByAgentIdAndProductId(agentId, payProduct.getId());
            if(agentPassage != null && agentPassage.getStatus() == MchConstant.PUB_YES) {
                agentRate = agentPassage.getAgentRate();
            }
            if(agentRate == null) {
                return returnFail(resultObject, "未设置二级代理商费率[agentPassage="+agentPassage+",agentRate="+agentRate+"]");
            }
            //处理一级代理商
            if(parentAgentId != null && parentAgentId != 0) {
                AgentPassage agentPassage2 = commonService.agentPassageService.findByAgentIdAndProductId(parentAgentId, payProduct.getId());
                if(agentPassage2 != null && agentPassage2.getStatus() == MchConstant.PUB_YES) {
                    parentAgentRate = agentPassage2.getAgentRate();
                }
                if(parentAgentRate == null) {
                    return returnFail(resultObject, "未设置一级代理商费率[agentPassage2="+agentPassage2+",parentAgentRate="+parentAgentRate+"]");
                }
            }
        }

        //判断私有商户的 账户余额是否充足
//        if(mchInfo.getType() == MchConstant.MCH_TYPE_PRIVATE){
//            MchAccount mchAccount = rpcCommonService.mchAccountService.findByMchId(mchId);
//            Long mchFee = mchAccount.getBalance() - mchAccount.getUnBalance();
//            long balanceAmount = balance.getAmount() - balance.getUnAmount() - balance.getFrozenAmount();
//            Long mchFee = XXPayUtil.calSingleOrderFee(mchFeeScale, amount);
//            if(mchFee > balanceAmount) return returnFail(resultObject, "可用余额不足");
//        }

        String mchTradeOrderId = MySeq.getTrade();
        MchTradeOrder mchTradeOrder = new MchTradeOrder();
        mchTradeOrder.setMchId(mchId);
        mchTradeOrder.setAppId(mchAppId);
        mchTradeOrder.setAmount(amount);
        mchTradeOrder.setProductId(productId+"");
        mchTradeOrder.setBody("二维码支付");
        mchTradeOrder.setGoodsId(qrcodeId+"");
        mchTradeOrder.setSubject(mchQrCode.getCodeName());
        mchTradeOrder.setTradeOrderId(mchTradeOrderId);
        mchTradeOrder.setChannelUserId(channelUserId);
        int result = commonService.mchTradeOrderService.add(mchTradeOrder);
        if(result <= 0 ) return returnFail(resultObject, "支付中心下单失败");

        // 验证参数通过,返回JSONObject对象
        PayOrder payOrder = new PayOrder();
        payOrder.setPayOrderId(MySeq.getPay());
        payOrder.setMchId(mchId);
        payOrder.setMchType(mchInfo.getType());
        payOrder.setAppId(mchAppId);
        payOrder.setMchOrderNo(mchTradeOrderId);
        payOrder.setAgentId(mchInfo.getAgentId());
        payOrder.setProductId(productId);                          // 支付产品ID
        payOrder.setProductType(payProduct.getProductType());    // 产品类型
        payOrder.setPassageId(payPassageAccount.getPayPassageId());                        // 支付通道ID
        payOrder.setPassageAccountId(passageAccountId);             // 支付通道账户ID
        payOrder.setChannelType(channelType);
        payOrder.setChannelId(channelId);
        payOrder.setAmount(amount);
        payOrder.setCurrency("CNY");
        payOrder.setClientIp(clintIp);
        payOrder.setDevice(null);
        payOrder.setSubject("二维码支付");
        payOrder.setBody("二维码支付");

        // 设置费率
        payOrder.setMchRate(mchPayPassage.getMchRate());
        payOrder.setChannelRate(passageAccount.getPassageRate());
        payOrder.setAgentRate(agentRate);
        payOrder.setParentAgentRate(parentAgentRate);
        payOrder.setPayPassageRate(payPassage.getPassageRate());

        JSONObject extraObject = new JSONObject();
        extraObject.put("openId", channelUserId);
        payOrder.setExtra(extraObject.toJSONString());
        payOrder.setChannelMchId(channelMchId);
        payOrder.setChannelUser(channelUserId);

        payOrder.setParam1(null);
        payOrder.setParam2(null);
        payOrder.setNotifyUrl(getDBApplicationConfig().getConfig4MchNotifyUrl());
        payOrder.setReturnUrl(null);

        //使用程序时间，保证创建时间与 过期时间的时间差计算结果一致
        Date nowTime = new Date();
        payOrder.setCreateTime(nowTime);
        payOrder.setExpireTime(DateUtils.addMinutes(nowTime, PayConstant.PAY_EXPIRE_TIME));


        result = commonService.payOrderService.createPayOrder(payOrder);
        if(result != 1) {
            _log.error("插入数据库失败, retObj={}", payOrder.getPayOrderId());
            return returnFail(resultObject, "支付中心下单失败");
        }

        //将订单号 更新至MchTradeOrder
        MchTradeOrder updateMchTradeOrder = new MchTradeOrder();
        updateMchTradeOrder.setTradeOrderId(mchTradeOrderId);
        updateMchTradeOrder.setPayOrderId(payOrder.getPayOrderId());
        result = commonService.mchTradeOrderService.update(updateMchTradeOrder);
        if(result <= 0 ) return returnFail(resultObject, "支付中心下单失败");

        String channelName = channelId.substring(0, channelId.indexOf("_"));
        AbstractRes retObj = localDynamicChannelService.callPaymentMethod(channelType, payOrder, true);
        resultObject.put("payParams", retObj.toJSONString());
        resultObject.put("state", "ok");

        return resultObject;
    }


    /**
     * 根据MchQrCode获取扫码支付渠道
     * @return
     */
    private String getChannelId(Long qrcodeId) {
        String ua = request.getHeader("User-Agent");
        // 无法识别扫码客户端
        if (StringUtils.isBlank(ua)) return null;
        String channelId = null;
        MchQrCode mchQrCode = commonService.mchQrCodeService.findById(qrcodeId);
        String channels = mchQrCode.getChannels();
        JSONArray channelArray = JSONArray.parseArray(channels);
        if(StringUtils.isBlank(channels) || CollectionUtils.isEmpty(channelArray)) {
            return null;
        }

        for(int i = 0; i < channelArray.size(); i++) {
            JSONObject c = channelArray.getJSONObject(i);
            channelId = c.getString("channelId");
            if(ua.contains("Alipay")) {
                if ("alipay_qr_h5".equals(channelId) || "alipay_wap".equals(channelId))
                return channelId;  //支付宝
            }else if(ua.contains("MicroMessenger") && "wxpay_jsapi".equals(channelId)) {
                return channelId; //微信公众号jsapi支付
            }
        }
        return channelId;
    }
}
